#from SistemaOperativo.Memory import Memory
from SistemaOperativo.Timer import Timer
from SistemaOperativo.Cpu import Cpu
from SistemaOperativo.Planificador import Planificador
from SistemaOperativo.PCB import PCB
from Signals.NewSignal import NewSignal
from SistemaOperativo.PlanificadorFCFS import PlanificadorFcFs
import sys
from Memory.ContinuosAsignment.MMU import MMU
from Memory.Memoria import Memoria
import time

class Kernel:
    #Al crearse una instancia de kernel se le pasa una politica de planificacion,
    #corriendo se inicializa en false porque todavia no arranco, cola de ready vacia,
    #cola de io vacia, una memoria de tamanio 100, PCBT un hash con pid y pcb, se crea un CPU
    #con memoria y kernel como parametro, current_pid en None porque todavia no arranco, 
    #modo kernel en false, pid_counter en 0, lastPid en 0, y se crea una MMU pasandole la 
    #memoria y la cpu como parametro
    def __init__(self, unaPolitica):
        self.corriendo = False  
        self.ready = []
        self.ioReady = []
        self.memory = Memoria(100)
        self.PCBT = {}
        self.CPU = Cpu(self.memory, self)
        self.current_pid = None
        self.modoKernel = False
        self.pid_counter = 0
        self.Planificador = unaPolitica
        self.lastPID = 0
        self.mmu = MMU(self.memory, self.CPU)
        
    # Getters & Setters ###
    def getCorriendo(self):
        return self.corriendo
    
    def setCorriendo(self, InitStop):
        self.corriendo = InitStop
    #######################
    
    def corriendo(self):
        return self.getCorriendo()
    
    #Arranca a correr el kernel, tambien arranca CPU
    def start(self):
        if not self.getCorriendo():
            self.setCorriendo(True)
            self.CPU.start()
            print('Corriendo el cpu')
            time.sleep(1)
    
    #Frena el kernel y tambien CPU
    def stop(self):
        self.setCorriendo(False)
        self.CPU.stop()
        sys.exit(0)
        
    #Interrupcion que recibe una senial como parametro y la ejecuta        
    def interrupt(self, aSignal):
        self.modoKernel = True
        try: 
            aSignal.ejecutar(self)
        finally:
            self.modoKernel = False
    
    #Empieza a correr un programa
    def run(self, prog):
        #Guarda un pid
        pid = self.pid_counter
        self.pid_counter = self.pid_counter + 1
        self.setCorriendo(True)
        #Si el pid no existe en la tabla de PCBs(PBCTable) lo agrega
        if not self.PCBT.__contains__(pid):
            self.PCBT[pid] = PCB(pid, 0, prog.getInstructionList().__len__(), prog.priority)
        #Hace el swapp in del programa en memoria
        self.mmu.swappIn(prog, self.PCBT[pid])
        #Se agrega el pid al planificador
        self.Planificador.addTask(pid, self)
        #Se interrumpe con la senial "NewSignal" para comenzar a a correr otro programa
        self.interrupt(NewSignal())
        time.sleep(len(prog.getInstructionList()))
        self.mmu.swappOut(prog, self.PCBT[pid])
        
            
    def reschedule(self):
        print(" -- Rescheduleando -- ")
        time.sleep(1)
        self.Planificador.reschedule(self)           
        
    def IsUserMode(self):
        return not self.modoKernel
    
    def terminarProceso(self):
        print("Termino el Programa")
        self.Planificador.terminarProceso()
        self.CPU.pc = None
           
        
